#pragma once

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <unordered_map>
using namespace std;

const string HttpSep = "\r\n";

class HttpRequest
{
private:
    string Analyzing(string &request)
    {
        int pos = request.find(HttpSep);
        if (pos == string::npos)
            return "";

        string line = request.substr(0, pos);
        request.erase(0, pos + HttpSep.size());
        return line;
    }

    void ParseReqLine(string &req_line)
    {
        stringstream ss(req_line);
        ss >> _method >> _url >> _version;
    }

public:
    HttpRequest() : _empty_line(HttpSep) {}

    bool Deserialize(string &request)
    {
        _req_line = Analyzing(request);
        if (_req_line.empty())
            return false;

        ParseReqLine(_req_line);

        while (true)
        {
            _req_header.push_back(Analyzing(request));
            if (_req_header.back().empty())
                break;
        }

        _content = request;
        return true;
    }

    void Debug()
    {
        // cout << "Request Line---> " << _req_line << endl;
        cout << "Method---> " << _method << endl;
        cout << "Url---> " << _url << endl;
        cout << "Version---> " << _version << endl;
        for (auto &e : _req_header)
        {
            cout << e << endl;
        }
        cout << "Content---> " << _content << endl;
    }

    string Url()
    {
        return _url;
    }

    ~HttpRequest() {}

private:
    string _req_line;
    vector<string> _req_header;
    string _empty_line;
    string _content;

    string _method;
    string _url;
    string _version;
};

const string root = "./web";
const string SuffixSep = ".";
const string ReqLineSep = " ";

class HttpResponse
{

    string Path(const string &url)
    {
        if (url == "/")
            return root + "/" + "index.html";
        else
            return root + url;
    }

    string GetContent(const string &path)
    {
        string content;
        ifstream file(path, ios::binary);
        if (!file)
        {
            _code = "404";
            _code_desc = _code_desc_hash["404"];
            string notfoundpath = root + "/" + "text/404.html";
            return GetContent(notfoundpath);
        }
        else
        {
            file.seekg(0, ios::end);
            streamsize size = file.tellg();
            content.resize(size);

            file.seekg(0, ios::beg);
            if (file.read((char *)content.c_str(), size))
            {
                return content;
            }
        }
        // ifstream file(path);
        // if (!file)
        //     return "";

        // string line;
        // string content;
        // while (getline(file, line))
        // {
        //     content += line;
        // }

        // return content;
        return "";
    }

    string Type(const string &url)
    {
        int pos = url.rfind(SuffixSep);
        if (pos == string::npos)
            return "";
        string suffix = url.substr(pos + SuffixSep.size());
        if (_type.count(suffix))
        {
            return _type[suffix];
        }
        else
            return "Unknown";
    }

public:
    HttpResponse() : _version("HTTP/1.0"), _code("200"), _code_desc("ok")
    {
        _type.insert(make_pair("html", "text/html"));
        _type.insert(make_pair("png", "image/png"));

        _code_desc_hash.insert(make_pair("200", "ok"));
        _code_desc_hash.insert(make_pair("404", "Not Found"));
    }

    string Response(HttpRequest &req)
    {
        string content = GetContent(Path(req.Url()));
        // _code = "307";
        // _code_desc = "Temporary Redirect";
        string httpstatusline = _version + ReqLineSep + _code + _code_desc + HttpSep;
        string content_type = "Content-Type: " + Type(Path(req.Url())) + HttpSep;
        string user_cookie = "Set-Cookie: user=123456789" + HttpSep;
        string passwd_cookie = "Set-Cookie: passwd=123456" + HttpSep;
        // string location = "Location: " + string("43.140.248.105:8889/text/404.html") + HttpSep;
        string content_len = "Content-Length: " + to_string(content.size()) + HttpSep;
        content_len += HttpSep;

        string response = httpstatusline + content_type + user_cookie + passwd_cookie + content_len + content;
        // string response = httpstatusline + content_type + /*location +*/ content_len + content;

        // cout << "Response#####" << response << endl;
        return response;
    }

    ~HttpResponse() {}

private:
    string _version;
    string _code;
    string _code_desc;

    unordered_map<string, string> _type;
    unordered_map<string, string> _code_desc_hash;
};